using System;
using System.Collections.Generic;
using System.Text;
using BizTalkCop.Rules.Bases;
using Microsoft.FxCop.Sdk;
using BizTalkCop.Rules.Enums;
using System.Xml.XPath;
using BizTalkCop.Rules.Helpers;

namespace BizTalkCop.Rules.Naming.Rules
{
    /// <summary>
    /// Rule verifying orchestration shapes are correctly prefixed
    /// </summary>
    /// <remarks>
    /// Checks shape prefixes for: scope, receive, send, call, start, throw, parallel, 
    /// delay, suspend, terminate, assign, listen, loop, call rules and compensate
    /// </remarks>
    public class OrchestrationShapesShouldHaveCorrectPrefix : OrchestrationRuleBase
    {
        /// <summary>
        /// Default constructor
        /// </summary>
        public OrchestrationShapesShouldHaveCorrectPrefix() : base("OrchestrationShapesShouldHaveCorrectPrefix") { }

        private Dictionary<ShapeType, string> _shapePrefixes;
        private Dictionary<ShapeType, string> ShapePrefixes
        {
            get
            {
                if (_shapePrefixes == null)
                {
                    _shapePrefixes = new Dictionary<ShapeType, string>();
                    foreach (string settingName in this.Settings.Keys)
                    {
                        ShapeType elementType = EnumHelper.Parse<ShapeType>(settingName);
                        if (elementType != ShapeType.Null)
                        {
                            _shapePrefixes.Add(elementType, this.Settings[settingName].ToString());
                        }
                    }
                }
                return _shapePrefixes;
            }
        }

        /// <summary>
        /// Checks if the orchestration's shapes are correctly prefixed
        /// </summary>
        /// <param name="type">Type to check</param>
        /// <returns>Problem collection</returns>
        public override ProblemCollection Check(TypeNode type)
        {
            if (IsOrchestration(type))
            {
                foreach (ShapeType shapeType in ShapePrefixes.Keys)
                {
                    XPathNodeIterator shapes = GetShapeInfos(type, shapeType);
                    this.CheckShapes(shapes, shapeType, type);
                }
            }
            return Problems;
        }

        private void CheckShapes(XPathNodeIterator shapes, ShapeType shapeType, TypeNode type)
        {
            foreach (XPathNavigator shapeNavigator in shapes)
            {
                string shapeName = shapeNavigator.SelectSingleNode(XPathQuery.ShapeTextNode).Value;
                string prefix = ShapePrefixes[shapeType];
                if (!shapeName.StartsWith(prefix))
                {
                    Problems.Add(new Problem(GetNamedResolution("Prefix", shapeName, prefix), type));
                }
            }            
        }

        #region Configured settings

        static OrchestrationShapesShouldHaveCorrectPrefix()
        {
            Dictionary<string, string> defaults = new Dictionary<string, string>();
            defaults.Add(Setting.ScopeShape, Default.ScopeShape);
            defaults.Add(Setting.ReceiveShape, Default.ReceiveShape);
            defaults.Add(Setting.SendShape, Default.SendShape);
            defaults.Add(Setting.CallShape, Default.CallShape);
            defaults.Add(Setting.ExecShape, Default.ExecShape);
            defaults.Add(Setting.ThrowShape, Default.ThrowShape);
            defaults.Add(Setting.ParallelShape, Default.ParallelShape);
            defaults.Add(Setting.DelayShape, Default.DelayShape);
            defaults.Add(Setting.SuspendShape, Default.SuspendShape);
            defaults.Add(Setting.TerminateShape, Default.TerminateShape);
            defaults.Add(Setting.MessageAssignmentShape, Default.MessageAssignmentShape);
            defaults.Add(Setting.ListenShape, Default.ListenShape);
            defaults.Add(Setting.WhileShape, Default.WhileShape);
            defaults.Add(Setting.CallRulesShape, Default.CallRulesShape);
            defaults.Add(Setting.CompensationShape, Default.CompensationShape);
            SetDefaults(typeof(OrchestrationShapesShouldHaveCorrectPrefix), defaults);
        }

        private struct Setting
        {
            public const string ScopeShape = "ScopeShape";
            public const string ReceiveShape = "ReceiveShape";
            public const string SendShape = "SendShape";
            public const string CallShape = "CallShape";
            public const string ExecShape = "ExecShape";
            public const string ThrowShape = "ThrowShape";
            public const string ParallelShape = "ParallelShape";
            public const string DelayShape = "DelayShape";
            public const string SuspendShape = "SuspendShape";
            public const string TerminateShape = "TerminateShape";
            public const string MessageAssignmentShape = "MessageAssignmentShape";
            public const string ListenShape = "ListenShape";
            public const string WhileShape = "WhileShape";
            public const string CallRulesShape = "CallRulesShape";
            public const string CompensationShape = "CompensationShape";
        }

        private struct Default
        {
            public const string ScopeShape = "Scope_";
            public const string ReceiveShape = "Rcv_";
            public const string SendShape = "Snd_";
            public const string CallShape = "Call_";
            public const string ExecShape = "Start_";
            public const string ThrowShape = "Throw_";
            public const string ParallelShape = "Parallel_";
            public const string DelayShape = "Delay_";
            public const string SuspendShape = "Suspend_";
            public const string TerminateShape = "Terminate_";
            public const string MessageAssignmentShape = "Assign_";
            public const string ListenShape = "Listen_";
            public const string WhileShape = "Loop_";
            public const string CallRulesShape = "CallRules_";
            public const string CompensationShape = "Compensate_";
        }

        #endregion
    }
}
